package com.yuke.cloud.common.core.aspect;


import com.yuke.cloud.common.base.enums.ErrorCodeEnum;
import com.yuke.cloud.common.base.exception.BusinessException;
import com.yuke.cloud.common.core.annotation.NoRepeatSubmit;
import com.yuke.cloud.common.util.PublicUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;

/*
 * <p>Company: Copyright (c) 2018. yuke All Rights Reserved. </br> </p>
 * <p>Title：yukemall </br> </p>
 * <p>com.yuke.cloud.common.core.aspect Description TODO </br> </p>
 * @Author wg
 * @Date   2019/4/8 16:20
 */
@Aspect
@Component
@Slf4j
public class NoRepeatSubmitAop {

    @Autowired
    private RedisTemplate<Object, Object> template;

    @Around("execution(* com.yuke.cloud.service.*.controller..*.*(..)) && @annotation(nrs)")
    public Object arround(ProceedingJoinPoint pjp, NoRepeatSubmit nrs) {
        ValueOperations<Object, Object> opsForValue = template.opsForValue();
        try {
            // 根据token进行验证对比，对于没有token的暂时不做处理
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            String sessionId = RequestContextHolder.getRequestAttributes().getSessionId(); // 对于前后端分离的项目，不一定能获取到sessionId,因此优先用token而不用sessionId来判断
            HttpServletRequest request = attributes.getRequest();

            String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
            String authPrefix = "Bearer ";
            String token = "";
            if (PublicUtil.isNotEmpty(authHeader) && StringUtils.startsWithIgnoreCase(authHeader, authPrefix)) {
                token = StringUtils.substring(authHeader, authPrefix.length());
            }

            if (PublicUtil.isEmpty(token)) {
                token = request.getParameter("accessToken");  // 刷新token时用accessToken传入token
            }

            if (PublicUtil.isEmpty(token)) {
                token = request.getParameter("token");
            }

            if (PublicUtil.isNotEmpty(token)) {
                sessionId = token;
            }

            String key = sessionId + "-" + request.getServletPath();

            // 对于重复刷新token的请求，直接将上次的结果返回
            if (request.getServletPath().contains("/auth/user/refreshToken")) {
                synchronized(this) {
                    Object oRedis = opsForValue.get(key);
                    if (oRedis == null) {// 如果缓存中有这个url视为重复提交
                        Object o = pjp.proceed();
                        opsForValue.set(key, o, 2, TimeUnit.SECONDS);  // 设置两秒内不能重复提交同样的请求
                        return o;
                    } else {
                        log.error("重复刷新token");
                        return oRedis;
//                return null;
                    }
                }
            } else {
                if (opsForValue.get(key) == null) {// 如果缓存中有这个url视为重复提交
                    opsForValue.set(key, 0, 2, TimeUnit.SECONDS);  // 设置两秒内不能重复提交同样的请求
                    Object o = pjp.proceed();
                    return o;
                } else {
                    log.error("重复提交");
                    throw new BusinessException(ErrorCodeEnum.GL99990003);
//                return null;
                }
            }
        } catch (Throwable e) {
            e.printStackTrace();
            log.error("验证重复提交时出现未知异常，token或sessionId可能为空!");
//            return "{\"code\":-889,\"message\":\"验证重复提交时出现未知异常!\"}";
            throw new BusinessException(ErrorCodeEnum.GL99990500);
        }

    }
}
